# -*- coding=utf-8
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
from qcloud_cos import CosServiceError
from qcloud_cos import CosClientError
from qcloud_cos.cos_threadpool import SimpleThreadPool

import sys
import os
from io import BytesIO
import logging

import inspect
from typing import Literal, List
from .cosmodel import *


class Cos:
    """腾讯云cos对象存储连接对象"""

    def __init__(
        self, bucket: Literal["bio-mercury-1316961827"], num_threads: int = 10
    ):

        # 正常情况日志级别使用 INFO，需要定位时可以修改为 DEBUG，此时 SDK 会打印和服务端的通信信息
        logging.basicConfig(level=logging.INFO, stream=sys.stdout)

        # 1. 设置用户属性, 包括 secret_id, secret_key, region等。Appid 已在 CosConfig 中移除，请在参数 Bucket 中带上 Appid。Bucket 由 BucketName-Appid 组成
        secret_id = "AKIDjJh3C7pJcw528LSUJvUyFHCwTnuoVegu"  # 用户的 SecretId，建议使用子账号密钥，授权遵循最小权限指引，降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
        secret_key = "LciaKE1jGcAdBOXVFE7yomlbNMDye8Xl"  # 用户的 SecretKey，建议使用子账号密钥，授权遵循最小权限指引，降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
        region = "ap-guangzhou"  # 替换为用户的 region，已创建桶归属的 region 可以在控制台查看，https://console.cloud.tencent.com/cos5/bucket
        # COS 支持的所有 region 列表参见 https://cloud.tencent.com/document/product/436/6224
        token = None  # 如果使用永久密钥不需要填入 token，如果使用临时密钥需要填入，临时密钥生成和使用指引参见 https://cloud.tencent.com/document/product/436/14048
        scheme = "https"  # 指定使用 http/https 协议来访问 COS，默认为 https，可不填

        config = CosConfig(
            Region=region,
            SecretId=secret_id,
            SecretKey=secret_key,
            Token=token,
            Scheme=scheme,
        )
        
        self.client = CosS3Client(config)
        self.pool = SimpleThreadPool(num_threads=num_threads)
        self.bucket = bucket
        frame_info = inspect.stack()[1]
        self.calling_file_path = frame_info.filename[::-1].split("\\", 1)[1][::-1]

    def uploads(self, uploadDir: str, cover: bool = False):
        """
        批量上传文件

        :param uploadDir: 上传文件夹路径
        :return: None
        """
        uploadDir_ = os.path.join(
            self.calling_file_path, uploadDir.strip(".").strip("/")
        )
        g = os.walk(uploadDir_)
        for _, _, file_list in g:
            for file_name in file_list:
                fileDir = os.path.join(uploadDir_, file_name)
                cosObjectKey = os.path.join(uploadDir, file_name)
                # 判断 COS 上文件是否存在
                exists = False
                try:
                    exists = self.client.object_exists(
                        Bucket=self.bucket, Key=cosObjectKey
                    )
                    if cover and exists:
                        self.client.delete_object(self.bucket, Key=cosObjectKey)
                except CosServiceError as e:
                    if e.get_status_code() == 404:
                        exists = False
                    else:
                        print("Error happened, reupload it.")
                print("File %s not exists in cos, upload it" % cosObjectKey)
                self.pool.add_task(
                    self.client.upload_file,
                    self.bucket,
                    cosObjectKey,
                    fileDir,
                )
        self.pool.wait_completion()
        result = self.pool.get_result()
        if not result["success_all"]:
            print("Not all files upload successed. you should retry")

    def upload(self, key: str, body: BytesIO) -> str:
        """
        上传文件

        :param key: 文件路径
        :param body: 文件流
        :return: 文件路径
        """
        self.client.upload_file_from_buffer(Bucket=self.bucket, Key=key, Body=body)
        return self.client._conf.uri(self.bucket, key)

    def getObsjects(
        self, Prefix: str, max_keys: int = 100, marker: str = ""
    ) -> List[objectInfoModel]:
        """
        获取文件列表
        :param Prefix: 前缀
        :param max_keys: 每页最大数
        :param marker: 页码，默认全部不用填
        """
        # 分页列举桶内对象，每个分页10个对象
        objectList: List[objectInfoModel] = []
        while True:
            response = self.client.list_objects(
                Bucket=self.bucket,
                Prefix=Prefix,
                Marker=marker,
                MaxKeys=max_keys,
            )
            if "Contents" in response:
                for content in response["Contents"]:
                    objectList.append(content["Key"])
                    print(content["Key"])

            if response["IsTruncated"] == "false":
                break

            marker = response["NextMarker"]
        return objectList

    def deleteObject(self, Key: str):
        """
        删除单个对象

        :param Key: 对象唯一值key
        :return: 删除结果
        """
        print("删除对象：{}".format(Key))
        return self.client.delete_object(Bucket=self.bucket, Key=Key)

    def deleteObjects(self, prefix: str = None, Delete: List[str] = []):
        """
        批量删除对象

        tip: 优先删除deleteList中的对象，如果列表为空，则删除所有指定`prefix`前缀的对象

        :param prefix: 删除对象前缀
        :param Delete: 删除对象Key列表
        :return: 删除结果
        """
        if Delete:
            print(",".join(Delete))
            self.client.delete_objects(
                Bucket=self.bucket,
                Delete={"Object": [{"Key": key} for key in Delete]},
            )
        else:

            marker = ""
            while True:
                response = self.client.list_objects(
                    Bucket=self.bucket, Prefix=prefix, Marker=marker
                )
                if "Contents" in response:
                    for content in response["Contents"]:
                        print("删除对象: ", content["Key"])
                        self.client.delete_object(
                            Bucket=self.bucket, Key=content["Key"]
                        )

                if response["IsTruncated"] == "false":
                    break

                marker = response["NextMarker"]
